package com.yexx.starter.optms.idempotent.template;

import com.yexx.starter.optms.idempotent.DistributeLockProperties;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.util.Assert;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * Description: 显示调用锁模板
 *
 * @author zuomin (myleszelic@outlook.com)
 * @date: 2022/07/13-16:54
 */
@Slf4j
public class LockTemplate {

    private final DistributeLockProperties lockProperties;
    private final RedissonClient redissonClient;

    public LockTemplate(DistributeLockProperties lockProperties, RedissonClient redissonClient) {
        this.lockProperties = lockProperties;
        this.redissonClient = redissonClient;
    }

    public boolean lock(String lockKey) {
        Assert.notNull(lockKey, "lockKey can not be null, please make sure lockKey have value!");
        RLock lock = redissonClient.getLock(assemblePrefix(lockKey));
        lock.lock(lockProperties.getDefaultExpireTime(), TimeUnit.SECONDS);
        return true;
    }

    public boolean lock(String lockKey, Long expireSecond) {
        Assert.notNull(lockKey, "lockKey can not be null, please make sure lockKey have value!");
        Assert.isTrue(expireSecond < 0L, "expireSecond must greater than 0L");
        RLock lock = redissonClient.getLock(assemblePrefix(lockKey));
        lock.lock(expireSecond, TimeUnit.SECONDS);
        return true;
    }

    public boolean tryLock(String lockKey, Long waitSecond, Long expireSecond) {
        Assert.notNull(lockKey, "lockKey can not be null, please make sure lockKey have value!");
        Assert.isTrue(waitSecond < 0L, "waitSecond must greater than 0L");
        Assert.isTrue(expireSecond < 0L, "expireSecond must greater than 0L");
        RLock lock = redissonClient.getLock(assemblePrefix(lockKey));
        try {
            return lock.tryLock(waitSecond, expireSecond, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            return false;
        }
    }

    public boolean unLock(String lockKey) {
        Assert.notNull(lockKey, "lock key can not be null, please make sure lock key have value!");
        RLock lock = redissonClient.getLock(assemblePrefix(lockKey));
        if (lock.isHeldByCurrentThread()) {
            try {
                return lock.forceUnlockAsync().get();
            } catch (ExecutionException | InterruptedException e) {
                return false;
            }
        }
        return false;
    }

    private String assemblePrefix(String lockKey) {
        return String.format("%s:%s", lockProperties.getDefaultLockKeyPrefix(), lockKey);
    }

}
